﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Caching;
using System.Web.Mvc;
using System.Web.Security;
using System.Xml.Linq;
using ColorSelectorMVC.Filters;
using ColorSelectorMVC.Models;
using WebMatrix.WebData;
using System.Diagnostics;

namespace ColorSelectorMVC.Controllers
{
    /// <summary>
    /// The color selector controller
    /// </summary>
    [InitializeSimpleMembership]
    public class ColorController : Controller
    {
        /// <summary>
        /// Session key for the selected color [anonymous user]
        /// </summary>
        private const string Selectedcolor = "SelectedColor";

        /// <summary>
        /// The cache key for colors
        /// </summary>
        private const string Colorscachename = "Colors";

        /// <summary>
        /// The xml node name of the colors node in the xml file
        /// </summary>
        private const string Colorxmlnode = "Color";

        /// <summary>
        /// The location of the colors xml file
        /// </summary>
        private const string Colorsfilepath = @"~/App_Data/colors.xml";

        /// <summary>
        /// The database context for the users
        /// </summary>
        private readonly UsersContext db = new UsersContext();

        /// <summary>
        /// Gets the valid colors
        /// </summary>
        /// <returns>Collection of colors</returns>
        private IEnumerable<SelectListItem> GetValidColors()
        {
            Cache cache = HttpRuntime.Cache;
            var colors = cache[Colorscachename] as Collection<SelectListItem>;
            try
            {
                if (colors == null)
                {
                    var data = new Collection<SelectListItem>();
                    string path = HttpContext.Server.MapPath(Colorsfilepath);

                    //Gets the color elements within the xml document
                    XDocument xDoc = XDocument.Load(path);
                    IEnumerable<XElement> elements = from ele in xDoc.Descendants()
                                                     where ele.Name.LocalName == Colorxmlnode
                                                     select ele;
                    foreach (XElement ele in elements)
                    {
                        data.Add(new SelectListItem { Text=ele.Value, Value = ele.Value});
                    }

                    //Creates the cache so that it is dependent on colors xml file
                    HttpRuntime.Cache.Insert(Colorscachename, data, new CacheDependency(path));
                    colors = cache[Colorscachename] as Collection<SelectListItem>;
                }
            }
            catch
            {
                Debug.WriteLine("Failed to parse the color xml file");
            }
            finally
            {
                //Return empty collection if colors are null
                colors = colors ?? new Collection<SelectListItem>();
            }

            return colors;
        }

        /// <summary>
        /// Gets the current user profile
        /// </summary>
        /// <returns>Current User Profile</returns>
        private UserProfile GetCurrentUserProfile()
        {
            UserProfile userprofile = null;
            if (User != null && User.Identity.IsAuthenticated)
            {
                MembershipUser user = Membership.GetUser(HttpContext.User.Identity.Name);
                if (user != null && user.ProviderUserKey != null)
                {
                    var userId = (int)user.ProviderUserKey;
                    userprofile = db.UserProfiles.Find(userId);
                }
            }

            return userprofile;
        }

        /// <summary>
        /// Goes through here when it a Get is called on the controller
        /// </summary>
        /// <returns>A view of the color selector</returns>
        public ActionResult Index()
        {
            UserProfile userprofile = GetCurrentUserProfile();

            SettingsModel sModel = null;

            //If user is logged in, create the settings model based off of the profile,  otherwise use 
            //anonymous cookie based approach
            if (userprofile != null)
            {
                sModel = new SettingsModel
                    {
                        SelectedColor = userprofile.SelectedColor,
                        UserName = userprofile.UserName
                    };
            }
            else
            {
                sModel = new SettingsModel
                    {
                        SelectedColor = Session[Selectedcolor] as string ?? string.Empty,
                        UserName = "Anonymous"
                    };
            }

            sModel.SelectableColors = GetValidColors();

            return View(sModel);
        }

        //
        // POST: /Color/Edit/5
        /// <summary>
        /// Goes through method when the color selection form is posted
        /// </summary>
        /// <param name="settingsModel">The settings model which should only contain the selected color coming from the client</param>
        /// <returns>View</returns>
        [HttpPost]
        public ActionResult Index(SettingsModel settingsModel)
        {
            UserProfile userprofile = GetCurrentUserProfile();

            //The first attempt at validation will always fail since the selectable colors is not sent
            //down to the client. This will clear the validation and do a second round after the valid
            // colors have been assigned.
            settingsModel.SelectableColors = GetValidColors();
            ModelState.Clear();
            TryValidateModel(settingsModel);

            //If the validation passes, it will save to the session or the db depending on whether the 
            //user is anonymous or not.
            if (ModelState.IsValid)
            {
                if (userprofile != null)
                {
                    userprofile.SelectedColor = settingsModel.SelectedColor;
                    db.Entry(userprofile).State = EntityState.Modified;
                    db.SaveChanges();
                }
                else
                {
                    Session[Selectedcolor] = settingsModel.SelectedColor;
                }

                return RedirectToAction("Index", "Home");
            }
            
            return View(settingsModel);
        }

        /// <summary>
        /// Disposes of any resources
        /// </summary>
        /// <param name="disposing">Whether its disposing or not</param>
        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}